<?php

namespace app\backmanage\controller;

use app\common\model\common\PartitionPackage as Model;
use thans\layuiAdmin\facade\Json;
use thans\layuiAdmin\facade\Utils;
use thans\layuiAdmin\Form;
use think\facade\View;
use thans\layuiAdmin\Traits\FormActions;
use think\Request;

class ProjectPartitionConfig extends Base
{
    use FormActions;

    protected $route_alias = 'projectPartitionConfig';
    protected $title       = '项目分区配置管理';

    public function list(Request $request)
    {
        if ($request->isAjax()||$request->isPost()) {
            list($where, $order, $page, $limit) = Utils::buildParams('id|partition_name|create_time');

            $model  = new Model();
            $where[] = ['status','=',1];
            if (checkSystemProductionEnv()){
                $where[] = ['is_env','in',[1,2,3]];
            }else{
                $where[] = ['is_env','=',4];
            }
            $list  = $model->where($where)->page($page, $limit)->order('partition_id','asc')->field('*')->select();
            $count = $model->where($where)->count();
            
            // 检查是否需要实时状态检测
            $checkStatus = $request->param('check_status', 0);
            
            //整理衍生数据
            foreach ($list as $k=>$v){
                $list[$k]['mysql_info'] = sprintf("数据库地址：%s<br>数据库库名：%s<br>数据库用户名：%s<br>数据库密码：%s<br>数据库端口：%s",$v['api_mysql_host_name'],$v['api_mysql_database'],$v['api_mysql_username'],$v['api_mysql_password'],$v['api_mysql_hostport']);
                $list[$k]['redis_info'] = sprintf("数据库地址：%s<br>数据库通用库名：%s<br>数据库队列库名：%s<br>数据库密码：%s<br>数据库端口：%s",$v['api_redis_host_name'],$v['api_redis_database'],$v['api_redis_database_queue'],$v['api_redis_password'],$v['api_redis_hostport']);
                $list[$k]['mongo_info'] = sprintf("数据库地址：%s<br>数据库库名：%s<br>数据库用户名：%s<br>数据库密码：%s<br>数据库端口：%s",$v['api_mongo_host_name'],$v['api_mongo_database'],$v['api_mongo_username'],$v['api_mongo_password'],$v['api_mongo_hostport']);
                $list[$k]['url']=sprintf("国内API：%s<br>海外API：%s<br>CRM：%s<br>PC：%s<br>H5初始地址：%s",$v['api_url'],$v['abroad_api_url'],$v['crm_url'],$v['pc_url'],$v['h5_url']);
                
                if ($checkStatus) {
                    // 只有明确要求检查状态时才执行连接测试
                    $this->checkConnectionStatus($list[$k], $v);
                } else {
                    // 默认显示未检测状态
                    $list[$k]['allocation_num'] = '-';
                    $list[$k]['vip_num'] = '-';
                    $list[$k]['mysql_status'] = 2; // 2表示未检测
                    $list[$k]['redis_status'] = 2;
                    $list[$k]['mongo_status'] = 2;
                }
            }
            Json::success('获取成功', $list, ['total' => $count,'count' => $count]);
        }
        $data = [];
        $data['cols'] = [
            ['field' => 'partition_id', 'title' => '分区编号', 'width' => 100],
            ['field' => 'partition_name', 'title' => '分区名称', 'width' => 100],
            ['field' => 'mysql_info', 'title' => 'Mysql信息', 'width' => 250],
            ['field' => 'mysql_status', 'title' => 'Mysql状态', 'type' => 'button', 'option' => [[1, '已连接', 'normal'], [0, '连接失败', 'danger'], [2, '未检测', 'warm']], 'width' => 100],
            ['field' => 'redis_info', 'title' => 'Redis信息', 'width' => 250],
            ['field' => 'redis_status', 'title' => 'Redis状态', 'type' => 'button', 'option' => [[1, '已连接', 'normal'], [0, '连接失败', 'danger'], [2, '未检测', 'warm']], 'width' => 100],
            ['field' => 'mongo_info', 'title' => 'Mongo信息', 'width' => 250],
            ['field' => 'mongo_status', 'title' => 'Mongo状态', 'type' => 'button', 'option' => [[1, '已连接', 'normal'], [0, '连接失败', 'danger'], [2, '未检测', 'warm']], 'width' => 100],
            ['field' => 'url', 'title' => '域名地址', 'width' => 250],
            ['field' => 'allocation_num', 'title' => '注册主播数', 'width' => 100],
            ['field' => 'vip_num', 'title' => '有效会员主播数', 'width' => 120],
            ['field' => 'send_key', 'title' => '企微KEY', 'width' => 150],
            ['field' => 'is_public', 'title' => '是否共用', 'type' => 'button', 'option' => [[1, '公用', 'normal'], [2, '不公用', 'danger']], 'width' => 100],
            ['field' => 'is_default', 'title' => '默认状态', 'type' => 'button', 'option' => [[1, '默认', 'normal'], [2, '普通', 'warm']], 'width' => 100],
            ['field' => 'is_env', 'title' => '环境', 'type' => 'button', 'option' => [[1, '正式', 'normal'], [2, '提审', 'warm'], [3, '预发', 'danger'], [4, '测试', 'info']], 'width' => 100],
            ['field' => 'create_time', 'title' => '创建时间', 'width' => 150],
            ['field' => 'update_time', 'title' => '更新时间', 'width' => 150],
            ['field' => 'partition_remark', 'title' => '分区备注', 'width' => 100],
            ['field' => 'right', 'title' => '操作', 'toolbar' => '#barDemo', 'width' =>460, 'fixed' => 'right'],
        ];
    
        $data['title'] = $this->title;
    
        // 添加搜索功能
        $data['search'][] = ['type' => 'text', 'field' => 'keyword', 'desc' => '请输入分区名称或编号'];
        $data['search'][] = ['type' => 'select', 'field' => 'is_public', 'desc' => '公用状态', 'option' => [1 => '公用', 2 => '不公用']];
        $data['search'][] = ['type' => 'select', 'field' => 'is_env', 'desc' => '环境', 'option' => [1 => '正式', 2 => '提审', 3 => '预发', 4 => '测试']];
        
        // URL配置
        $data['list_url'] = '/backmanage/' . $this->route_alias . '/list';
        
        $data['create_url'] = '/backmanage/' . $this->route_alias . '/create';
        $data['edit_url'] = '/backmanage/' . $this->route_alias . '/edit';
        $data['del_url'] = '/backmanage/' . $this->route_alias . '/delete';
        $data['sync_url'] = '/backmanage/config/sync';
        // 在list方法中添加同步数据库的URL配置
        $data['sync_sql_url'] = '/backmanage/' . $this->route_alias . '/syncSql';
        // 添加状态检测URL
        $data['check_status_url'] = '/backmanage/' . $this->route_alias . '/checkStatus';
        // 添加批量检测状态URL
        $data['batch_check_status_url'] = '/backmanage/' . $this->route_alias . '/batchCheckStatus';
        
        return View::fetch('projectPartitionConfig/index', $data);
    }

    public function delete(Request $request)
    {
        if($request->isDelete()){
            $param=request()->param();
            if (!isset($param['partition_id'])){
                Json::error('获取分区ID失败，请重试');
            }
            //获取子服务数据库配置
            $model  = new Model();
            $info=$model->where([
                'partition_id'=>$param['partition_id'],
                'status'=>1
            ])->find();
            if(empty($info->id)){
                Json::error('分区ID无效，请重试');
            }
            $info=$info->toArray();
            //获取该分区下，目前存在主播手机号信息
            $lists=\app\common\model\common\Anchor::where('partition_id',$param['partition_id'])
                    ->where('status',1)->column('mobile','id')?:[];
            $mobiles=array_values($lists);
            $anchorIds=array_keys($lists);
            if($mobiles){
                $subMemberModel=getSwitchDatabaseModel(sprintf("partition_%s",$info['partition_id']),$info['api_mysql_host_name'],$info['api_mysql_database'],$info['api_mysql_username'],$info['api_mysql_password'],$info['api_mysql_hostport']);
                $where[]=['mobile','in',$mobiles];
                $where[] = ['status','=',1];
                $ids=$model->table('member')->column('id');
                if(count($ids)!=count($mobiles)){
                    Json::error('分区数据存在差异，请同步后再操作');
                }
                $subMemberModel->where('id','in',$ids)->update([
                    'status'     =>2,
                    'update_time'=>time()
                ]);
            }
            $res = Model::where('id','in',$anchorIds)->update([
                'status'=>0,
                'update_time'=>time()
            ]);
            return json(['code' => 0, 'message' => '删除成功(同步注销主播'.count($anchorIds).'人)', 'data' => $res]);
        }
    }

    private function buildForm()
    {
        $model = new Model();
        $form  = new Form($model);
        $allocatePartitionIds=self::getAllocatePartitionIds();
        $partitionOptions=[];
        for ($i=0; $i <\app\common\lib\Consts::REGION_ZONE_TOTAL_NUM ; $i++) { 
            $partitionOptions[]=['val' => $i, 'title' => sprintf("分区-%s (%s)",$i,in_array($i,$allocatePartitionIds)?"已使用":"未使用")];
        }
        $form->select()->name('partition_id')->label('分区号')->options($partitionOptions)->rules('required');  
        $form->text()->name('partition_name')->label('分区名称')->placeholder('请输入分区名称')->rules('required');
        $form->text()->name('partition_remark')->label('分区备注')->placeholder('请输入分区备注');
        $form->text()->name('api_url')->label('API域名地址')->placeholder('请输入API域名地址')->rules('url');
        $form->text()->name('abroad_api_url')->label('海外API域名地址')->placeholder('请输入海外API域名地址')->rules('url');
        $form->text()->name('crm_url')->label('CRM后台网址')->placeholder('请输入CRM后台网址')->rules('url');
        $form->text()->name('crm_name')->label('CRM登录账号')->placeholder('请输入CRM登录账号');
        $form->text()->name('crm_pwd')->label('CRM登录密码')->placeholder('请输入CRM登录密码');
        $form->text()->name('pc_url')->label('PC域名地址')->placeholder('请输入PC域名地址')->rules('url');
        $form->text()->name('h5_url')->label('H5域名地址')->placeholder('请输入H5域名地址')->rules('url');

        $form->text()->name('api_mysql_host_name')->label('Mysql-地址')->placeholder('请输入Mysql-地址');
        $form->text()->name('api_mysql_database')->label('Mysql-库名')->placeholder('请输入Mysql-库名');
        $form->text()->name('api_mysql_username')->label('Mysql-用户名')->placeholder('请输入Mysql-用户名');
        $form->text()->name('api_mysql_password')->label('Mysql-密码')->placeholder('请输入Mysql-密码');
        $form->text()->name('api_mysql_hostport')->label('Mysql-端口')->placeholder('请输入Mysql-端口');
        $form->text()->name('api_mysql_params')->label('Mysql-额外参数')->placeholder('请输入Mysql-额外参数');

        $form->text()->name('api_mongo_host_name')->label('Mongo-地址')->placeholder('请输入Mongo-地址');
        $form->text()->name('api_mongo_database')->label('Mongo-库名')->placeholder('请输入Mongo-库名');
        $form->text()->name('api_mongo_username')->label('Mongo-用户名')->placeholder('请输入Mongo-用户名');
        $form->text()->name('api_mongo_password')->label('Mongo-密码')->placeholder('请输入Mongo-密码');
        $form->text()->name('api_mongo_hostport')->label('Mongo-端口')->placeholder('请输入Mongo-端口');

        $form->text()->name('api_redis_host_name')->label('Redis-地址')->placeholder('请输入Redis-地址');
        $form->text()->name('api_redis_password')->label('Redis-密码')->placeholder('请输入Redis-密码');
        $form->text()->name('api_redis_hostport')->label('Redis-端口')->placeholder('请输入Redis-端口');

        $form->text()->name('send_key')->label('企微KEY')->placeholder('请输入企微KEY');

        $status[] = ['val' => 2, 'title' => '普通'];
        $status[] = ['val' => 1, 'title' => '默认'];
        $form->select()->name('is_default')->label('默认状态')->options($status);
        $publicStatus[] = ['val' => 2, 'title' => '不公用'];
        $publicStatus[] = ['val' => 1, 'title' => '公用'];
        $form->select()->name('is_public')->label('公用状态')->options($publicStatus);
        if (checkSystemProductionEnv()){
            $envStatus[]=['val' => 1, 'title' => '正式'];
            $envStatus[]=['val' => 2, 'title' => '提审'];
            $envStatus[]=['val' => 3, 'title' => '预发'];
        }else{
            $envStatus[]=['val' => 4, 'title' => '测试'];
        }
        $form->select()->name('is_env')->label('环境')->options($envStatus);
        return $form;
    }


    public function update($id, Request $request)
    {
        $param = $request->param();
        $this->buildForm($request)->update($id, $param);
    }

    public static function getAllocatePartitionIds()
    {
        //获取目前库内已存在分区ID组
        $model = new Model();
        $partitionIds=$model->where('status',1)->column('partition_id');
        return $partitionIds;
    }

    public function save(Request $request)
    {
        $param = $request->param();
        if (!isset($param['partition_id'])){
            Json::error('获取分区ID失败，请重试');
        }
        if(in_array($param['partition_id'],self::getAllocatePartitionIds())){
             Json::error('该分区已被使用，请重新选择');
        }
        //计算分区相关配置信息
        $param['api_redis_database']=calculateRedisRegionDbNum($param['partition_id'],1);

        $this->buildForm($request)->save($param);
    }

        public function syncSql(Request $request)
    {
        if ($request->isPost()) {
            set_time_limit(0);
            $param = $request->param();
            
            // 记录开始日志
            \think\facade\Log::info('开始同步数据库: ' . json_encode($param, JSON_UNESCAPED_UNICODE));
            
            if (!isset($param['partition_id'])) {
                \think\facade\Log::error('同步数据库失败：获取分区ID失败: ' . json_encode($param, JSON_UNESCAPED_UNICODE));
                return json(['code' => -1, 'message' => '获取分区ID失败，请重试']);
            }
            
            // 获取分区配置信息
            $model = new Model();
            $partitionInfo = $model->where([
                'partition_id' => $param['partition_id'],
                'status' => 1
            ])->find();
            
            if (empty($partitionInfo->id)) {
                \think\facade\Log::error('同步数据库失败：分区ID无效: partition_id=' . $param['partition_id']);
                return json(['code' => -1, 'message' => '分区ID无效，请重试']);
            }
            
            $partitionData = [
                'partition_id' => $param['partition_id'],
                'host' => $partitionInfo['api_mysql_host_name'],
                'database' => $partitionInfo['api_mysql_database'],
                'port' => $partitionInfo['api_mysql_hostport']
            ];
            \think\facade\Log::info('获取分区配置信息成功: ' . json_encode($partitionData, JSON_UNESCAPED_UNICODE));
            
            try {
                // 读取SQL文件内容
                $sqlFilePath = root_path() . 'extend/database/base.sql';
                if (!file_exists($sqlFilePath)) {
                    \think\facade\Log::error('同步数据库失败：SQL文件不存在: file_path=' . $sqlFilePath);
                    return json(['code' => -1, 'message' => 'SQL文件不存在：' . $sqlFilePath]);
                }
                
                $sqlContent = file_get_contents($sqlFilePath);
                if ($sqlContent === false) {
                    \think\facade\Log::error('同步数据库失败：读取SQL文件失败: file_path=' . $sqlFilePath);
                    return json(['code' => -1, 'message' => '读取SQL文件失败']);
                }
                
                \think\facade\Log::info('读取SQL文件成功: file_size=' . strlen($sqlContent));
                
                // 建立数据库连接
                $host = $partitionInfo['api_mysql_host_name'];
                $username = $partitionInfo['api_mysql_username'];
                $password = $partitionInfo['api_mysql_password'];
                $port = $partitionInfo['api_mysql_hostport'];
                $database = $partitionInfo['api_mysql_database'];
                
                $connectionInfo = [
                    'host' => $host,
                    'port' => $port,
                    'database' => $database,
                    'username' => $username
                ];
                \think\facade\Log::info('开始建立数据库连接: ' . json_encode($connectionInfo, JSON_UNESCAPED_UNICODE));
                
                // 创建PDO连接（不指定数据库，用于检查和创建数据库）
                $dsn = "mysql:host={$host};port={$port};charset=utf8mb4";
                $pdo = new \PDO($dsn, $username, $password, [
                    \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                    \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
                ]);
                
                \think\facade\Log::info('数据库连接成功');
                
                // 检查数据库是否存在
                $dbExists = $this->checkDatabaseExists($pdo, $database);
                \think\facade\Log::info('检查数据库存在性: database=' . $database . ':exists=' . ($dbExists ? 'true' : 'false'));
                
                if (!$dbExists) {
                    // 创建数据库
                    $createDbSql = "CREATE DATABASE `{$database}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci";
                    $pdo->exec($createDbSql);
                    \think\facade\Log::info('创建数据库成功: database=' . $database);
                }
                
                // 选择数据库
                $pdo->exec("USE `{$database}`");
                \think\facade\Log::info('选择数据库成功: database=' . $database);
                
                // 解析SQL文件，提取表结构
                $tableDefinitions = $this->parseTableDefinitions($sqlContent);
                \think\facade\Log::info('解析SQL文件完成: table_count=' . count($tableDefinitions));
                
                $successCount = 0;
                $errorCount = 0;
                $errors = [];
                $operations = [];
                
                // 处理每个表
                foreach ($tableDefinitions as $tableName => $tableDefinition) {
                    \think\facade\Log::info('开始处理表: table_name=' . $tableName);
                    
                    try {
                        // 检查表是否存在
                        $tableExists = $this->checkTableExists($pdo, $tableName);
                        \think\facade\Log::info('检查表存在性: table_name=' . $tableName . ':exists=' . ($tableExists ? 'true' : 'false'));
                        
                        if (!$tableExists) {
                            // 表不存在，创建表
                            $pdo->exec($tableDefinition['create_sql']);
                            $operations[] = "创建表: {$tableName}";
                            $successCount++;
                            \think\facade\Log::info('创建表成功: table_name=' . $tableName);
                        } else {
                            // 表存在，检查结构差异
                            $differences = $this->compareTableStructure($pdo, $tableName, $tableDefinition);
                            \think\facade\Log::info('检查表结构差异: table_name=' . $tableName . ':differences_count=' . count($differences));
                            
                            if (!empty($differences)) {
                                // 执行结构变更
                                foreach ($differences as $alterSql) {
                                    $pdo->exec($alterSql);
                                    $operations[] = "修改表结构: {$tableName} - " . substr($alterSql, 0, 100);
                                    $successCount++;
                                    \think\facade\Log::info('执行表结构变更: table_name=' . $tableName . ':alter_sql=' . $alterSql);
                                }
                            } else {
                                $operations[] = "表结构无变化: {$tableName}";
                                \think\facade\Log::info('表结构无变化: table_name=' . $tableName);
                            }
                        }
                        
                    } catch (\PDOException $e) {
                        $errorCount++;
                        $errorMsg = "处理表 {$tableName} 失败: " . $e->getMessage();
                        $errors[] = $errorMsg;
                        $errorInfo = [
                            'table_name' => $tableName,
                            'error' => $e->getMessage(),
                            'code' => $e->getCode()
                        ];
                        \think\facade\Log::error('处理表失败: ' . json_encode($errorInfo, JSON_UNESCAPED_UNICODE));
                        
                        // 如果错误太多，停止执行
                        if ($errorCount > 10) {
                            $errors[] = "错误过多，停止执行...";
                            \think\facade\Log::error('错误过多，停止执行: error_count=' . $errorCount);
                            break;
                        }
                    }
                }
                
                // 构建返回消息
                $message = "数据库同步完成！";
                if (!$dbExists) {
                    $message .= " 创建了数据库: {$database}。";
                }
                
                // 限制操作详情的长度，避免JSON编码问题
                $operationSummary = array_slice($operations, 0, 3);
                $operationText = implode('; ', array_map(function($op) {
                    // 限制每个操作描述的长度
                    return mb_strlen($op) > 100 ? mb_substr($op, 0, 100) . '...' : $op;
                }, $operationSummary));
                
                $message .= " 操作详情: " . $operationText;
                if (count($operations) > 3) {
                    $message .= " 等共" . count($operations) . "项操作";
                }
                
                $resultInfo = [
                    'success_count' => $successCount,
                    'error_count' => $errorCount,
                    'operations' => $operations
                ];
                \think\facade\Log::info('数据库同步完成: ' . json_encode($resultInfo, JSON_UNESCAPED_UNICODE));
                
                if ($errorCount > 0) {
                    $errorInfo = [
                        'success_count' => $successCount,
                        'error_count' => $errorCount,
                        'errors' => $errors
                    ];
                    \think\facade\Log::error('同步完成但有错误: ' . json_encode($errorInfo, JSON_UNESCAPED_UNICODE));
                    
                    // 限制错误信息的长度
                    $errorSummary = array_slice($errors, 0, 2);
                    $errorText = implode('; ', array_map(function($err) {
                        return mb_strlen($err) > 80 ? mb_substr($err, 0, 80) . '...' : $err;
                    }, $errorSummary));
                    
                    return json(['code' => -1, 'message' => "同步完成，但有错误。成功: {$successCount}, 失败: {$errorCount}。错误信息: " . $errorText]);
                } else {
                    // 确保消息不会太长，避免JSON编码问题
                    if (mb_strlen($message) > 200) {
                        $message = mb_substr($message, 0, 200) . '...';
                    }
                    return json(['code' => 0, 'message' => $message]);
                }
                
            } catch (\PDOException $e) {
                $connectionError = [
                    'host' => $host ?? '',
                    'port' => $port ?? '',
                    'database' => $database ?? '',
                    'error' => $e->getMessage(),
                    'code' => $e->getCode()
                ];
                \think\facade\Log::error('数据库连接失败: ' . json_encode($connectionError, JSON_UNESCAPED_UNICODE));
                return json(['code' => -1, 'message' => '数据库连接失败: ' . $e->getMessage()]);
            } catch (\Exception $e) {
                $exceptionInfo = [
                    'error' => $e->getMessage(),
                    'code' => $e->getCode(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ];
                \think\facade\Log::error('同步失败: ' . json_encode($exceptionInfo, JSON_UNESCAPED_UNICODE));
                return json(['code' => -1, 'message' => '同步失败: ' . $e->getMessage()]);
            }
        }
        
        \think\facade\Log::error('请求方法错误: method=' . $request->method());
        return json(['code' => -1, 'message' => '请求方法错误']);
    }

    /**
     * 检查数据库是否存在
     */
    private function checkDatabaseExists($pdo, $database)
    {
        $stmt = $pdo->prepare("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?");
        $stmt->execute([$database]);
        return $stmt->fetch() !== false;
    }
    
    /**
     * 检查表是否存在
     */
    private function checkTableExists($pdo, $tableName)
    {
        $stmt = $pdo->prepare("SHOW TABLES LIKE ?");
        $stmt->execute([$tableName]);
        return $stmt->fetch() !== false;
    }
    
    /**
     * 解析SQL文件，提取表定义
     */
    private function parseTableDefinitions($sqlContent)
    {
        $tableDefinitions = [];
        
        // 移除SQL注释
        $sqlContent = preg_replace('/--.*$/m', '', $sqlContent);
        $sqlContent = preg_replace('/\/\*.*?\*\//s', '', $sqlContent);
        
        // 匹配CREATE TABLE语句
        preg_match_all('/CREATE TABLE\s+(?:IF NOT EXISTS\s+)?`?(\w+)`?\s*\((.*?)\)\s*(?:ENGINE.*?)?(?:;|$)/is', $sqlContent, $matches, PREG_SET_ORDER);
        
        foreach ($matches as $match) {
            $tableName = $match[1];
            $tableBody = $match[2];
            
            // 解析字段定义
            $fields = $this->parseTableFields($tableBody);
            
            $tableDefinitions[$tableName] = [
                'create_sql' => $match[0],
                'fields' => $fields
            ];
        }
        
        return $tableDefinitions;
    }
    
    /**
     * 解析表字段定义
     */
    private function parseTableFields($tableBody)
    {
        $fields = [];
        
        // 先按行分割，然后重新组合可能跨行的字段定义
        $lines = preg_split('/\r\n|\r|\n/', $tableBody);
        $currentField = '';
        
        foreach ($lines as $line) {
            $line = trim($line);
            if (empty($line)) continue;
            
            // 移除行末的逗号
            $line = rtrim($line, ',');
            
            // 如果行以反引号开始，说明是新字段的开始
            if (preg_match('/^`\w+`/', $line)) {
                // 处理之前的字段
                if (!empty($currentField)) {
                    $this->parseFieldDefinition($currentField, $fields);
                }
                $currentField = $line;
            } else {
                // 继续当前字段的定义
                $currentField .= ' ' . $line;
            }
        }
        
        // 处理最后一个字段
        if (!empty($currentField)) {
            $this->parseFieldDefinition($currentField, $fields);
        }
        
        return $fields;
    }
    
    /**
     * 解析单个字段定义
     */
    private function parseFieldDefinition($fieldLine, &$fields)
    {
        $fieldLine = trim($fieldLine);
        
        // 跳过约束定义
        if (stripos($fieldLine, 'PRIMARY KEY') !== false || 
            stripos($fieldLine, 'KEY ') !== false || 
            stripos($fieldLine, 'INDEX ') !== false ||
            stripos($fieldLine, 'CONSTRAINT') !== false ||
            stripos($fieldLine, 'UNIQUE') !== false) {
            return;
        }
        
        // 匹配字段定义：`field_name` type [constraints]
        if (preg_match('/^`(\w+)`\s+(.+)$/i', $fieldLine, $matches)) {
            $fieldName = $matches[1];
            $fieldDefinition = trim($matches[2]);
            
            // 清理字段定义，移除多余的空格
            $fieldDefinition = preg_replace('/\s+/', ' ', $fieldDefinition);
            
            $fields[$fieldName] = $fieldDefinition;
        }
    }
    
    /**
     * 比较表结构差异
     */
    private function compareTableStructure($pdo, $tableName, $tableDefinition)
    {
        $alterStatements = [];
        
        try {
            // 获取当前表结构
            $stmt = $pdo->prepare("DESCRIBE `{$tableName}`");
            $stmt->execute();
            $currentFields = [];
            
            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
                $fieldName = $row['Field'];
                $fieldType = $row['Type'];
                $nullable = $row['Null'] === 'YES' ? 'NULL' : 'NOT NULL';
                $default = '';
                if ($row['Default'] !== null) {
                    if (is_numeric($row['Default'])) {
                        $default = "DEFAULT {$row['Default']}";
                    } else {
                        $default = "DEFAULT '{$row['Default']}'";
                    }
                }
                $extra = $row['Extra'];
                
                $currentDef = trim("{$fieldType} {$nullable} {$default} {$extra}");
                $currentFields[$fieldName] = preg_replace('/\s+/', ' ', $currentDef);
            }
            
            // 比较字段
            foreach ($tableDefinition['fields'] as $fieldName => $expectedDefinition) {
                // 验证字段定义不为空
                if (empty($expectedDefinition) || empty(trim($expectedDefinition))) {
                    \think\facade\Log::warning("跳过空字段定义: table_name={$tableName}:field_name={$fieldName}");
                    continue;
                }
                
                if (!isset($currentFields[$fieldName])) {
                    // 字段不存在，需要添加
                    $alterSql = "ALTER TABLE `{$tableName}` ADD COLUMN `{$fieldName}` {$expectedDefinition}";
                    $alterStatements[] = $alterSql;
                    \think\facade\Log::info("生成添加字段SQL: {$alterSql}");
                } else {
                    // 字段存在，比较定义
                    $currentDef = strtolower(trim($currentFields[$fieldName]));
                    $expectedDef = strtolower(trim($expectedDefinition));
                    
                    // 标准化定义进行比较
                    $currentDef = $this->normalizeFieldDefinition($currentDef);
                    $expectedDef = $this->normalizeFieldDefinition($expectedDef);
                    
                    if ($currentDef !== $expectedDef) {
                        // 字段定义不同，需要修改
                        $alterSql = "ALTER TABLE `{$tableName}` MODIFY COLUMN `{$fieldName}` {$expectedDefinition}";
                        $alterStatements[] = $alterSql;
                        \think\facade\Log::info("生成修改字段SQL: {$alterSql}");
                        \think\facade\Log::info("字段差异比较: current={$currentDef}:expected={$expectedDef}");
                    }
                }
            }
            
        } catch (\Exception $e) {
            \think\facade\Log::error("比较表结构失败: table_name={$tableName}:error=" . $e->getMessage());
        }
        
        return $alterStatements;
    }
    
    /**
     * 标准化字段定义用于比较
     */
    private function normalizeFieldDefinition($definition)
    {
        // 移除多余空格
        $definition = preg_replace('/\s+/', ' ', trim($definition));
        
        // 标准化一些常见的差异
        $definition = str_replace(['utf8mb4_general_ci', 'utf8mb4_unicode_ci'], 'utf8mb4', $definition);
        $definition = str_replace('character set utf8mb4 collate utf8mb4', '', $definition);
        $definition = str_replace('auto_increment', '', $definition);
        
        // 移除多余空格
        $definition = preg_replace('/\s+/', ' ', trim($definition));
        
        return $definition;
    }
    
    /**
     * 解析SQL文件，分割成单独的SQL语句（保留原方法作为备用）
     */
    private function parseSqlFile($sqlContent)
    {
        // 移除SQL注释
        $sqlContent = preg_replace('/--.*$/m', '', $sqlContent);
        $sqlContent = preg_replace('/\/\*.*?\*\//s', '', $sqlContent);
        
        // 按分号分割SQL语句
        $statements = explode(';', $sqlContent);
        
        $validStatements = [];
        foreach ($statements as $statement) {
            $statement = trim($statement);
            
            // 跳过空语句和特殊命令
            if (empty($statement) || 
                stripos($statement, 'SET NAMES') === 0 ||
                stripos($statement, 'SET FOREIGN_KEY_CHECKS') === 0 ||
                stripos($statement, 'BEGIN') === 0 ||
                stripos($statement, 'COMMIT') === 0) {
                continue;
            }
            
            $validStatements[] = $statement;
        }
        
        return $validStatements;
    }

    /**
     * 插入表数据（如果数据不存在）
     */
    private function insertTableData($pdo, $tableName, $sqlContent, &$operations, &$successCount, &$errorCount, &$errors)
    {
        try {
            // 从SQL内容中提取该表的INSERT语句
            $insertStatements = $this->extractInsertStatements($sqlContent, $tableName);
            
            if (empty($insertStatements)) {
                \think\facade\Log::info('未找到表的INSERT语句: table_name=' . $tableName);
                return;
            }
            
            $insertedCount = 0;
            $skippedCount = 0;
            
            foreach ($insertStatements as $insertSql) {
                // 解析INSERT语句，提取字段和值
                $insertData = $this->parseInsertStatement($insertSql);
                
                if (!$insertData) {
                    \think\facade\Log::warning('解析INSERT语句失败: sql=' . substr($insertSql, 0, 200));
                    continue;
                }
                
                // 检查数据是否已存在（基于主键或唯一键）
                $exists = $this->checkDataExists($pdo, $tableName, $insertData);
                
                if (!$exists) {
                    // 数据不存在，使用预处理语句执行插入
                    $this->executeInsertStatement($pdo, $insertData);
                    $insertedCount++;
                    \think\facade\Log::info('插入数据成功: table_name=' . $tableName . ':id=' . (isset($insertData['values'][0]) ? $insertData['values'][0] : 'unknown'));
                } else {
                    $skippedCount++;
                    \think\facade\Log::info('数据已存在，跳过插入: table_name=' . $tableName . ':id=' . (isset($insertData['values'][0]) ? $insertData['values'][0] : 'unknown'));
                }
            }
            
            if ($insertedCount > 0 || $skippedCount > 0) {
                $operations[] = "处理表数据: {$tableName} (新增:{$insertedCount}, 跳过:{$skippedCount})";
                $successCount += $insertedCount;
            }
            
        } catch (\PDOException $e) {
            $errorCount++;
            $errorMsg = "插入表数据失败 {$tableName}: " . $e->getMessage();
            $errors[] = $errorMsg;
            \think\facade\Log::error('插入表数据失败: table_name=' . $tableName . ':error=' . $e->getMessage());
        }
    }
    
    /**
     * 从SQL内容中提取指定表的INSERT语句
     */
    private function extractInsertStatements($sqlContent, $tableName)
    {
        $insertStatements = [];
        
        // 匹配该表的INSERT语句
        $pattern = '/INSERT\s+INTO\s+`?' . preg_quote($tableName, '/') . '`?\s*\([^)]+\)\s*VALUES\s*\([^)]+\);?/i';
        
        if (preg_match_all($pattern, $sqlContent, $matches)) {
            $insertStatements = $matches[0];
        }
        
        return $insertStatements;
    }
    
    /**
     * 解析INSERT语句，提取字段和值
     */
    private function parseInsertStatement($insertSql)
    {
        // 匹配INSERT语句的字段和值
        $pattern = '/INSERT\s+INTO\s+`?(\w+)`?\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i';
        
        if (preg_match($pattern, $insertSql, $matches)) {
            $tableName = $matches[1];
            $fieldsStr = $matches[2];
            $valuesStr = $matches[3];
            
            // 解析字段名
            $fields = array_map(function($field) {
                return trim($field, '` ');
            }, explode(',', $fieldsStr));
            
            // 解析值（这里简化处理，实际可能需要更复杂的解析）
            $values = $this->parseInsertValues($valuesStr);
            
            return [
                'table' => $tableName,
                'fields' => $fields,
                'values' => $values
            ];
        }
        
        return false;
    }
    
    /**
     * 解析INSERT语句中的值
     */
    private function parseInsertValues($valuesStr)
    {
        $values = [];
        $current = '';
        $inQuotes = false;
        $quoteChar = '';
        $length = strlen($valuesStr);
        
        for ($i = 0; $i < $length; $i++) {
            $char = $valuesStr[$i];
            
            if (!$inQuotes && ($char === "'" || $char === '"')) {
                // 开始引号
                $inQuotes = true;
                $quoteChar = $char;
                $current .= $char;
            } elseif ($inQuotes && $char === $quoteChar) {
                // 检查是否是转义的引号
                if ($i + 1 < $length && $valuesStr[$i + 1] === $quoteChar) {
                    // 转义的引号，跳过下一个字符
                    $current .= $char . $char;
                    $i++;
                } else {
                    // 结束引号
                    $inQuotes = false;
                    $quoteChar = '';
                    $current .= $char;
                }
            } elseif (!$inQuotes && $char === ',') {
                // 字段分隔符
                $values[] = trim($current);
                $current = '';
            } else {
                $current .= $char;
            }
        }
        
        // 添加最后一个值
        if (!empty($current)) {
            $values[] = trim($current);
        }
        
        // 清理值（移格外层引号，但保持内容不变）
        $cleanValues = [];
        foreach ($values as $value) {
            $value = trim($value);
            // 只移除最外层的引号
            if ((substr($value, 0, 1) === "'" && substr($value, -1) === "'") ||
                (substr($value, 0, 1) === '"' && substr($value, -1) === '"')) {
                $cleanValues[] = substr($value, 1, -1);
            } else {
                $cleanValues[] = $value;
            }
        }
        
        return $cleanValues;
    }
    
    /**
     * 检查数据是否已存在
     */
    private function checkDataExists($pdo, $tableName, $insertData)
    {
        try {
            // 获取表的主键字段
            $primaryKey = $this->getTablePrimaryKey($pdo, $tableName);
            
            if ($primaryKey && isset($insertData['fields']) && isset($insertData['values'])) {
                // 找到主键字段在INSERT语句中的位置
                $pkIndex = array_search($primaryKey, $insertData['fields']);
                
                if ($pkIndex !== false && isset($insertData['values'][$pkIndex])) {
                    $pkValue = $insertData['values'][$pkIndex];
                    
                    // 检查主键值是否已存在
                    $stmt = $pdo->prepare("SELECT COUNT(*) FROM `{$tableName}` WHERE `{$primaryKey}` = ?");
                    $stmt->execute([$pkValue]);
                    $count = $stmt->fetchColumn();
                    
                    return $count > 0;
                }
            }
            
            // 如果没有主键或无法确定，返回false（允许插入）
            return false;
            
        } catch (\PDOException $e) {
            \think\facade\Log::warning('检查数据存在性失败: table_name=' . $tableName . ':error=' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * 获取表的主键字段
     */
    private function getTablePrimaryKey($pdo, $tableName)
    {
        try {
            $stmt = $pdo->prepare("SHOW KEYS FROM `{$tableName}` WHERE Key_name = 'PRIMARY'");
            $stmt->execute();
            $result = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            return $result ? $result['Column_name'] : null;
            
        } catch (\PDOException $e) {
            \think\facade\Log::warning('获取表主键失败: table_name=' . $tableName . ':error=' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * 执行INSERT语句（使用预处理语句）
     */
    private function executeInsertStatement($pdo, $insertData)
    {
        $tableName = $insertData['table'];
        $fields = $insertData['fields'];
        $values = $insertData['values'];
        
        // 构建预处理SQL语句
        $fieldsList = '`' . implode('`, `', $fields) . '`';
        $placeholders = str_repeat('?,', count($values) - 1) . '?';
        $sql = "INSERT INTO `{$tableName}` ({$fieldsList}) VALUES ({$placeholders})";
        
        // 执行预处理语句
        $stmt = $pdo->prepare($sql);
        $stmt->execute($values);
    }

    /**
     * 检查连接状态（提取为独立方法）
     */
    private function checkConnectionStatus(&$listItem, $config)
    {
        // MySQL连接状态检测
        $model = getSwitchDatabaseModel(
            sprintf("partition_%s", $config['partition_id']),
            $config['api_mysql_host_name'],
            $config['api_mysql_database'],
            $config['api_mysql_username'],
            $config['api_mysql_password'],
            $config['api_mysql_hostport']
        );
        
        $mysqlStatus = 0;
        $num = 0;
        $vipNum = 0;
        
        if ($model) {
            $mysqlStatus = 1;
            try {
                $num = $model->table('member')->where('status', '=', 1)->where('vip_id', '>', 0)->count();
                $vipNum = $model->table('member')
                    ->where('status', '=', 1)
                    ->where('auth_state', '1')
                    ->where('vip_id', '>', 0)
                    ->where('vip_end_time', '>', time())
                    ->count();
            } catch (\Exception $e) {
                // 连接成功但查询失败，仍然标记为连接成功
                $num = 0;
                $vipNum = 0;
            }
        }
        
        // Redis连接状态检测（优化超时时间）
        $redisStatus = 0;
        try {
            $redis = new \Redis();
            $connected = $redis->connect($config['api_redis_host_name'], $config['api_redis_hostport'], 2); // 减少到2秒超时
            if ($connected) {
                if (!empty($config['api_redis_password'])) {
                    $auth = $redis->auth($config['api_redis_password']);
                    if (!$auth) {
                        throw new \Exception('Redis认证失败');
                    }
                }
                if (!empty($config['api_redis_database'])) {
                    $redis->select($config['api_redis_database']);
                }
                $redis->ping();
                $redisStatus = 1;
                $redis->close();
            }
        } catch (\Exception $e) {
            $redisStatus = 0;
        }
        
        // MongoDB连接状态检测（优化并处理类不存在的情况）
        $mongoStatus = 0;
        try {
            if (!class_exists('\MongoDB\Client')) {
                throw new \Exception('MongoDB PHP driver not installed');
            }
            
            $mongoUri = 'mongodb://';
            if (!empty($config['api_mongo_username']) && !empty($config['api_mongo_password'])) {
                $mongoUri .= urlencode($config['api_mongo_username']) . ':' . urlencode($config['api_mongo_password']) . '@';
            }
            $mongoUri .= $config['api_mongo_host_name'] . ':' . $config['api_mongo_hostport'];
            if (!empty($config['api_mongo_database'])) {
                $mongoUri .= '/' . $config['api_mongo_database'];
            }
            
            $mongoClient = new \MongoDB\Client($mongoUri, [
                'connectTimeoutMS' => 2000, // 减少到2秒连接超时
                'serverSelectionTimeoutMS' => 2000, // 减少到2秒服务器选择超时
            ]);
            
            $mongoClient->listDatabases();
            $mongoStatus = 1;
        } catch (\Exception $e) {
            $mongoStatus = 0;
        }
        
        $listItem['allocation_num'] = $num;
        $listItem['vip_num'] = $vipNum;
        $listItem['mysql_status'] = $mysqlStatus;
        $listItem['redis_status'] = $redisStatus;
        $listItem['mongo_status'] = $mongoStatus;
    }

    /**
     * 异步检查单个分区状态
     */
    public function checkStatus(Request $request)
    {
        $partitionId = $request->param('partition_id','');
        if ($partitionId==="") {
            Json::error('分区ID不能为空');
        }
        
        $model = new Model();
        $config = $model->where('partition_id', $partitionId)->where('status', 1)->find();
        if (!$config) {
            Json::error('分区配置不存在');
        }
        
        $result = [];
        $this->checkConnectionStatus($result, $config);
        
        Json::success('检测完成', $result);
    }

    /**
     * 批量检查所有分区状态
     */
    public function batchCheckStatus(Request $request)
    {
        $model = new Model();
        $where = [['status', '=', 1]];
        
        if (checkSystemProductionEnv()) {
            $where[] = ['is_env', 'in', [1, 2, 3]];
        } else {
            $where[] = ['is_env', '=', 4];
        }
        
        $list = $model->where($where)->select();
        $results = [];
        
        foreach ($list as $config) {
            $result = [];
            $this->checkConnectionStatus($result, $config);
            $results[$config['partition_id']] = $result;
        }
        
        Json::success('批量检测完成', $results);
    }
}